package com.asset.dispoal.file.judgment.service.command;

import com.asset.dispoal.file.judgment.dto.JudgmentBookFileDto;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Repository;
import org.springframework.web.multipart.MultipartFile;
import tech.mhuang.pacebox.core.exception.BusinessException;
import tech.mhuang.pacebox.core.file.FileUtil;
import tech.mhuang.pacebox.core.id.BaseIdeable;
import tech.mhuang.pacebox.core.io.IOUtil;
import tech.mhuang.pacebox.springboot.protocol.Result;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @description:
 * @author: yf
 * @date: 2022-12-13 16:13:55
 */
@Slf4j
@Repository
public class UploadJudgmentBookCommand {
    @Autowired
    private Environment environment;
    @Autowired
    private BaseIdeable<String> snowFlake;

    @SneakyThrows
    public Result<String> execute(JudgmentBookFileDto fileDto) {
        LocalDateTime now = LocalDateTime.now();
        String year = now.getYear() + "";
        String month = now.getMonthValue() + "";
        String day = now.getDayOfMonth() + "";
        String dir = environment.getProperty("file.evidence.url");
        String childUrl = environment.getProperty("file.judgmentBookFile.url");
        Path path = Paths.get(dir, childUrl, year, month, day);
        if (!Files.exists(path)) {
            Files.createDirectories(path);
        }
        if (fileDto.getTaskId() == null) {
            fileDto.setTaskId(snowFlake.generateId());
        }
        String fileName = null;
        try {
            fileName = singleUpload(fileDto.getChunk(), fileDto.getChunks(), fileDto.getTaskId(), fileDto.getUuid(), fileDto.getName(), path.toString(), fileDto.getFile());
        } catch (Exception e) {
            throw new BusinessException(Result.SYS_FAILD, "上传失败");
        }
        if ((Integer.parseInt(fileDto.getChunks()) - 1) == Integer.parseInt(fileDto.getChunk())) {
            String filePath = Paths.get(childUrl, year, month, day, fileName).toString();
            return Result.ok(filePath);
        }
        return Result.ok();
    }

    private String singleUpload(String chunk, String chunks, String task_id, String uuid, String name, String baseDir, MultipartFile file) throws IOException {
        // 分片文件存放包路径
        String targetDir = baseDir + name.split("\\.")[0] + uuid;
        // 分片文件包路径+文件名+文件类型
        String fileName = uuid + "-" + task_id + name;
        String targetFile = baseDir + File.separator + fileName;
        // 分片文件对象
        File chunkFile;
        // 总块数
        int chunksIntLabel = 0;
        // 当前块索引
        int chunkIntLabel = 0;
        if (chunks != null) {
            chunksIntLabel = Integer.parseInt(chunks);
        }
        if (chunk != null) {
            chunkIntLabel = Integer.parseInt(chunk);
        }
        if (task_id != null) {
            // 总块数大于0
            if (chunksIntLabel > 0) {
                File dir = new File(targetDir);
                // 文件包路径创建
                if (!dir.exists()) {
                    dir.mkdir();
                }
                String chunkFileName = task_id + "&&&&&" + chunkIntLabel;
                chunkFile = new File(targetDir, chunkFileName);
            } else {
                chunkFile = new File(targetFile);
            }
            FileOutputStream fs = new FileOutputStream(chunkFile);
            IOUtil.copy(file.getInputStream(), fs, true);
            if (chunksIntLabel > 0) {
                mergeFile(targetDir, targetFile, chunksIntLabel);
            }
        }
        return fileName;
    }

    /**
     * 合并文件
     *
     * @param fileDir
     * @param fileName
     * @param chunks
     * @return
     */
    private boolean mergeFile(String fileDir, String fileName, Integer chunks) {
        if (isAllUploaded(fileDir, chunks)) {
            try {
                List<FileInputStream> targetList = new CopyOnWriteArrayList<>();
                File parentFile = new File(fileDir);
                File[] files = parentFile.listFiles();
                for (int i = 0; i < chunks; i++) {
                    for (File file : files) {
                        if (file.getName().contains("&&&&&" + i)) {
                            FileInputStream sourceStream = new FileInputStream(file);
                            targetList.add(sourceStream);
                            break;
                        }
                    }
                }

                Enumeration<FileInputStream> targetEnum = Collections.enumeration(targetList);
                SequenceInputStream sequenceStream = new SequenceInputStream(targetEnum);
                // 合分片文件
                saveStreamToFile(sequenceStream, fileName);

                for (File file : files) {
                    if (file.isDirectory()) {
                        FileUtil.deleteDirectory(file);
                    } else {
                        FileUtil.delete(file);
                    }
                }
                FileUtil.deleteDirectory(parentFile);
                return true;
            } catch (Exception e) {
                log.error(e.getMessage());
                return false;
            }
        }
        return false;
    }

    /**
     * 检查文件是否全部上传完毕
     *
     * @param fileDir
     * @param chunk
     * @return
     */
    private Boolean isAllUploaded(String fileDir, int chunk) {
        File file = new File(fileDir);
        File[] files = file.listFiles();
        int fileLength = files.length;
        if (fileLength == chunk) {
            return true;
        }
        return false;
    }

    /**
     * 文件流写入
     *
     * @param inputStream
     * @param filePath
     * @throws Exception
     */
    private void saveStreamToFile(SequenceInputStream inputStream, String filePath) throws Exception {
        /* 创建输出流，写入数据，合并分块 */
        OutputStream outputStream = Files.newOutputStream(Paths.get(filePath));
        IOUtil.copy(inputStream, outputStream, true);
    }
}
